﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Satire.Common
{
    public class DynamicArray<T> where T : struct, IComparable<T>, IEquatable<T>
    {
        public const int DefaultCapacity = 10;
        public const int DefaultDelta = 10;
        public DynamicArray(int length, int capacity, int delta)
        {
            _initializeBuffer(length, capacity, delta);
        }
        public DynamicArray(int length, int capacity)
        {
            _initializeBuffer(length, capacity, DefaultCapacity);
        }
        public DynamicArray(int length)
        {
            _initializeBuffer(length, DefaultCapacity, DefaultDelta);
        }
        public DynamicArray()
        {
            _initializeBuffer(0, DefaultCapacity, DefaultDelta);
        }
        public T this[int i] 
        {
            get { return _value[i]; }
            set { _value[i] = value; }
        }
        public int Length { get { return _length; } }
        public int Capacity { get { if (_value == null) { return 0; } else { return _value.Length; } } }
        public int Delta { get { return _delta; } }
        public int Add(T ele)
        {
            int i = _length;
            _grow(1);
            _value[i] = ele;
            return i;
        }
        public int AddInSequence(T ele, bool duplicatesAllowed)
        {
            int x = Array.BinarySearch<T>(_value, 0, _length, ele);
            if (x > 0)
            {
                if (!duplicatesAllowed) { return x; }
            }
            else
            {
                x = ~x;
            }
            Insert(x, ele);
            return x;
        }
        public int Insert(int at, T ele)
        {
            at = _insert(at, 1);
            _value[at] = ele;
            return at + 1;
        }
        public void Reset()
        {
            _shrink(_length);
        }
        public void Union(DynamicArray<T> arr)
        {

            _union(arr._value, arr._length);
        }

        public void Union(T[] arr)
        {

            _union(arr, arr.Length);
          }
        #region private
        private int _insert(int idx, int count)
        {
            if (idx < 0) { idx = 0; }
            if (idx > _length) { idx = _length; }
            if (count < 0) { count = 0; }
            if (count == 0) { return idx; }

            _grow(count);
            _copy(_value, idx, _value, idx + count, _length - idx - count);
            return idx;
        }
        private void _initializeBuffer(int length, int capacity, int delta) 
        {
            if (length < 0) { length = 0; }
            if (delta < 0) { delta = 0; }
            if (capacity < 0) { capacity = 0; }
            if (capacity < length) { capacity = length; }
            _setCapacity(capacity);
            _length = length;
            _delta = delta;
        }
        private void _shrink(int s)
        {
            if (s <= 0)
            {
                return;
            }
            if (s >= _length)
            {
                s = _length;
            }
            _length -= s;
            if (_value != null)
            {
                if (_value.Length - _length <= _delta)
                {
                    return;
                }
            }
            _setCapacity(_length + _delta);
        }
        private void _grow(int s)
        {
            if (s <= 0)
            {
                return;
            }
            _length += s;
            if (_value != null)
            {
                if (_length <= _value.Length)
                {
                    return;
                }
            }
            _setCapacity(_length + _delta);
        }
        private void _setCapacity(int size)
        {
            if (size < 0)
            {
                return;
            }
            if (size == 0)
            {
                _value = null;
                return;
            }
            if (_value == null)
            {
                _value = new T[size];
                return;
            }
            T[] tempValue = new T[size];
            _copy(_value, 0, tempValue, 0, _min(_value.Length, tempValue.Length));
            _value = tempValue;
        }
        private void _copy(T[] arr1, int offset1, T[] arr2, int offset2, int count)
        {
            int bytes = (Buffer.ByteLength(arr1) / arr1.Length);
            Buffer.BlockCopy(arr1, offset1 * bytes, arr2, offset2 * bytes, count * bytes);
        }
        private int _min(int i1, int i2)
        {
            if (i1 < i2) { return i1; }
            return i2;
        }

        public void _union(T[] arr, int len)
        {
            int mIdx = 0;
            int cIdx = 0;

            while (cIdx < len)
            {
                int cv = -1;
                if (mIdx < _length)
                {
                    cv = arr[cIdx].CompareTo(_value[mIdx]);
                }
                if (cv < 0)
                {
                    mIdx = this.Insert(mIdx, arr[cIdx]);
                    cIdx++;
                }
                else
                {
                    if (cv == 0)
                    {
                        mIdx++;
                        cIdx++;
                    }
                    else
                    {
                        mIdx++;
                    }
                }
            }
        }


        private T[] _value;
        private int _delta;
        private int _length;
        #endregion
    }
}
